#include "resource_storage.h"
#include "base/logging.h"
#include "base/bind.h"
#include "base/file_util.h"
#include "base/stringprintf.h"
#include "utils.h"

namespace dl {
using namespace net;

// StorageFile
StorageFile::StorageFile(const StorageCreateInfo& info) {
  info_ = info;
}

StorageFile::~StorageFile() {
}

bool StorageFile::Initialize() {
  FilePath parent = info_.save_path.DirName();
  if (file_util::CreateDirectory(parent)) {
    return Open();
  }
  return false;
}

int64 StorageFile::TellPosition() const {
  DCHECK(file_ != INVALID_HANDLE_VALUE);
	return ::SetFilePointer(file_, NULL, NULL, FILE_CURRENT);
}

bool StorageFile::SeekPosition(int64 position) {
  DCHECK(file_ != INVALID_HANDLE_VALUE);
	return ::SetFilePointer(file_, (LONG)position, 0, FILE_BEGIN) != INVALID_SET_FILE_POINTER;
}

bool StorageFile::Open() {
  DCHECK(!info_.save_path.empty());
  FilePath path = info_.save_path.AddExtension(kUndownExtension);
  
	DWORD disposition = CREATE_ALWAYS;
	if ((info_.start_position > 0) && FileExists(path)) {
		disposition = OPEN_ALWAYS;
	}
	
	file_.Set(::CreateFileW(path.value().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,	
		                      NULL,	disposition, FILE_ATTRIBUTE_NORMAL,	NULL));

	if (file_ == INVALID_HANDLE_VALUE)
		return false;

  if (!SeekPosition(info_.start_position)) {
    SeekPosition(0);
  }
  return true;
}

bool StorageFile::AppendData(IOBufferWithSizePtr buffer) {
  if (file_ != INVALID_HANDLE_VALUE) {
		DWORD written;
		BOOL result = ::WriteFile(file_, buffer->data(), buffer->size(), &written, NULL);
		if (result && static_cast<int>(written) == buffer->size())
			return true;
  }
  return false;
}

bool StorageFile::Finished(URLRequestStatus stat) {
  file_.Close();
  FilePath path = info_.save_path.AddExtension(kUndownExtension);
  bool success = true;
  if (stat.is_success()) {
    success = file_util::Move(path, info_.save_path);
	} 

  return success;
}

// ResourceStorage
ResourceStorage::ResourceStorage(StorageDelegate* delegate) {
  delegate_ = delegate;
}

void ResourceStorage::CreateStorage(unsigned int symbol, const StorageCreateInfo& info) {
  DCHECK(CalledOnValidThread());
  StorageFilePtr file(new StorageFile(info));
  if (file->Initialize()) {
    storage_files_[symbol] = file;
    if (delegate_) {
      delegate_->OnCreateStorageCompleted(symbol, file->TellPosition());
    }
  }
}

void ResourceStorage::PositionStorage(unsigned int symbol, int64 start_position) {
  DCHECK(CalledOnValidThread());
  StorageFileMap::iterator iter = storage_files_.find(symbol);
  if (iter != storage_files_.end()) {
    StorageFilePtr& storage = iter->second;
    storage->SeekPosition(start_position);
  }
}

void ResourceStorage::WriteToStorage(unsigned int symbol, IOBufferWithSizePtr buffer) {
  DCHECK(CalledOnValidThread());
  StorageFileMap::iterator iter = storage_files_.find(symbol);
  if (iter != storage_files_.end()) {
    StorageFilePtr& storage = iter->second;
    storage->AppendData(buffer);
  }
}

void ResourceStorage::StorageFinished(unsigned int symbol, URLRequestStatus stat) {
  DCHECK(CalledOnValidThread());
  StorageFileMap::iterator iter = storage_files_.find(symbol);
  if (iter != storage_files_.end()) {
    StorageFilePtr& storage = iter->second;
    storage->Finished(stat);
  }
}

}
